<?php

Loader::import('pspframework.utils.Security');

class AuthComponent extends Object {

/**
用户当前在线状态
 */
	var $_loggedIn = false;

	var $components = array('session');

/**
指定给密码加密的对象实体
 */
	var $authenticate = null;

/**
使用那种方式进行权限验证比如，有actions,用在
 */
	var $authorize = false;

/**
假如登入时是使用ajax登入，则用此来设置是那个view element
 */
	var $ajaxLogin = null;

/**
 * The name of the element used for SessionComponent::setFlash
 */
	var $flashElement = 'default';

/**
使用的是那个数据模型来存储用户信息
 */
	var $userModel = 'user';

/**
设置查询条件，比如array('User.is_active' => 1).

 */
	var $userScope = array();

/**
用户数据表中的用户名，密码的字段名称
 */
	var $fields = array('username' => 'username', 'password' => 'password','otherfiledslogin'=>array('email','telphone'));

/**
其值为"Auth.{$userModel 的值}",比如Auth.User 其用来存储在session里面的变量名
 */
	var $sessionKey = null;

/**
 * If using action-based access control, this defines how the paths to action
 * ACO nodes is computed.  If, for example, all controller nodes are nested
 * under an ACO node named 'Controllers', $actionPath should be set to
 * "Controllers/".
 *
 * @var string
 * @access public
 */
	var $actionPath = null;

/**
 * A URL (defined as a string or array) to the controller action that handles
 * logins.
 *
 * @var mixed
 * @access public
 */
	var $loginAction = null;

/**
 * Normally, if a user is redirected to the $loginAction page, the location they
 * were redirected from will be stored in the session so that they can be
 * redirected back after a successful login.  If this session value is not
 * set, the user will be redirected to the page specified in $loginRedirect.
 *
 * @var mixed
 * @access public
 */
	var $loginRedirect = null;

/**
 * The default action to redirect to after the user is logged out.  While AuthComponent does
 * not handle post-logout redirection, a redirect URL will be returned from AuthComponent::logout().
 * Defaults to AuthComponent::$loginAction.
 *
 * @var mixed
 * @access public
 * @see AuthComponent::$loginAction
 * @see AuthComponent::logout()
 */
	var $logoutRedirect = null;

/**
 * The name of model or model object, or any other object has an isAuthorized method.
 *
 * @var string
 * @access public
 */
	var $object = null;

/**
登入错误提示信息
 */
	var $loginError = null;

/**
提示不是会员信息
 */
	var $authError = null;

/**
 * Determines whether AuthComponent will automatically redirect and exit if login is successful.
 *
 * @var boolean
 * @access public
 */
	var $autoRedirect = true;

/**
 * Controller actions for which user validation is not required.
 *
 * @var array
 * @access public
 * @see AuthComponent::allow()
 */
	var $allowedActions = array();

/**
 * Maps actions to CRUD operations.  Used for controller-based validation ($validate = 'controller').
 *
 * @var array
 * @access public
 * @see AuthComponent::mapActions()
 */
	var $actionMap = array(
		'index'		=> 'read',
		'add'		=> 'create',
		'edit'		=> 'update',
		'view'		=> 'read',
		'remove'	=> 'delete'
	);

/**
得到Controller::$data的值

 */
	var $data = array();

/**
得到Controller::$params的值

 */
	var $params = array();

/**
存储controller的方法名称
 */
	var $_methods = array();


	function initialize(&$controller, $settings = array()) {
		$this->params = $controller->params;
		$crud = array('create', 'read', 'update', 'delete');
		$this->actionMap = array_merge($this->actionMap, array_combine($crud, $crud));
		$this->_methods = $controller->methods;
		$prefixes = Router::prefixes();
		if (!empty($prefixes)) {
			foreach ($prefixes as $prefix) {
				$this->actionMap = array_merge($this->actionMap, array(
					$prefix . '_index' => 'read',
					$prefix . '_add' => 'create',
					$prefix . '_edit' => 'update',
					$prefix . '_view' => 'read',
					$prefix . '_remove' => 'delete',
					$prefix . '_create' => 'create',
					$prefix . '_read' => 'read',
					$prefix . '_update' => 'update',
					$prefix . '_delete' => 'delete'
				));
			}
		}
		$this->_set($settings);//用来设置该类的一些对象变量
	}


	function startup(&$controller) {
	
		$methods = array_flip($controller->methods);
		$action = strtolower($controller->params['action']);
		$isMissingAction = (
			$controller->scaffold === false &&
			!isset($methods[$action])
		);

		if ($isMissingAction) {
			return true;
		}

		if (!$this->__setDefaults()) {
			return false;
		}

		$this->data = $controller->data = $this->hashPasswords($controller->data);
		$url = '';

		if (isset($controller->params['url']['url'])) {
			$url = $controller->params['url']['url'];
		}
		$url = Router::normalize($url);
		$loginAction = Router::normalize($this->loginAction);

		$allowedActions = array_map('strtolower', $this->allowedActions);
		$isAllowed = (
			$this->allowedActions == array('*') ||
			in_array($action, $allowedActions)
		);

		if ($loginAction != $url && $isAllowed) {
			return true;
		}

		if ($loginAction == $url) {
			$model =& $this->getModel();
			if (empty($controller->data) || !isset($controller->data[$model->alias])) {
				if (!$this->C_session->check('Auth.redirect') && !$this->loginRedirect && Request::_env('HTTP_REFERER')) {
					$this->C_session->write('Auth.redirect', $controller->referer(null, true));
				}
				return false;
			}

			$isValid = !empty($controller->data[$model->alias][$this->fields['username']]) &&
				!empty($controller->data[$model->alias][$this->fields['password']]);

			if ($isValid) {
				$username = $controller->data[$model->alias][$this->fields['username']];
				$password = $controller->data[$model->alias][$this->fields['password']];

				$data = array(
					$model->alias . '.' . $this->fields['username'] => $username,
					$model->alias . '.' . $this->fields['password'] => $password
				);

				if ($this->login($data)) {
					if ($this->autoRedirect) {
						$controller->redirect($this->redirect(), null, true);
					}
					return true;
				}
			}

			$this->C_session->setFlash($this->loginError, $this->flashElement, array(), 'auth');
			$controller->data[$model->alias][$this->fields['password']] = null;
			return false;
		} else {		
			if (!$this->user()) {
				if (!Request::isAjax()) {
					$this->C_session->setFlash($this->authError, $this->flashElement, array(), 'auth');
					if (!empty($controller->params['url']) && count($controller->params['url']) >= 2) {
						$query = $controller->params['url'];
						unset($query['url'], $query['ext']);
						$url .= Router::queryString($query, array());
					}
					$this->C_session->write('Auth.redirect', $url);
					$controller->redirect($loginAction);
					return false;
				} elseif (!empty($this->ajaxLogin)) {
					$controller->viewPath = 'elements';
					echo $controller->render($this->ajaxLogin, $this->RequestHandler->ajaxLayout);
					$this->_stop();
					return false;
				} else {
					$controller->redirect(null, 403);
				}
			}
		}

		if (!$this->authorize) {
			return true;
		}

		extract($this->__authType());

		switch ($type) {
			case 'controller':
				$this->object =& $controller;
			break;
			case 'crud':
			case 'actions':
				if (isset($controller->C_acl)) {
					$this->Acl =& $controller->C_acl;
				} else {
					trigger_error('Could not find AclComponent. Please include Acl in Controller::$components.', E_USER_WARNING);
				}
			break;
			case 'model':
				if (!isset($object)) {
					$hasModel = (
						isset($controller->{$controller->modelClass}) &&
						is_object($controller->{$controller->modelClass})
					);
					$isUses = (
						!empty($controller->uses) && isset($controller->{$controller->uses[0]}) &&
						is_object($controller->{$controller->uses[0]})
					);

					if ($hasModel) {
						$object = $controller->modelClass;
					} elseif ($isUses) {
						$object = $controller->uses[0];
					}
				}
				$type = array('model' => $object);
			break;
		}
		
		if ($this->isAuthorized($type)) {
			return true;
		}

		$this->C_session->setFlash($this->authError, $this->flashElement, array(), 'auth');
		$controller->redirect($controller->referer(), null, true);
		return false;
	}

/**
从新设置默认的变量值
 */
	function __setDefaults() {
		if (empty($this->userModel)) {
			trigger_error("Could not find \$userModel. Please set AuthComponent::\$userModel in beforeFilter().", E_USER_WARNING);
			return false;
		}
		$model = $this->userModel;
		$defaults = array(
			'loginAction' => array(
				'controller' => $model,
				'action' => 'login',
			),
			'sessionKey' => 'Auth.' . $model,
			'logoutRedirect' => $this->loginAction,
			'loginError' => 'Login failed. Invalid username or password.',
			'authError' => 'You are not authorized to access that location.'
		);
		foreach ($defaults as $key => $value) {
			if (empty($this->{$key})) {
				$this->{$key} = $value;
			}
		}
		return true;
	}

/**
用户权限判断
 * Types:
 * 'controller' will validate against Controller::isAuthorized() if controller instance is
 * 				passed in $object
 * 'actions' will validate Controller::action against an AclComponent::check()
 * 'crud' will validate mapActions against an AclComponent::check()
 * 		array('model'=> 'name'); will validate mapActions against model
 * 		$name::isAuthorized(user, controller, mapAction)
 * 'object' will validate Controller::action against
 * 		object::isAuthorized(user, controller, action)
 *
 * @param string $type Type of authorization
 * @param mixed $object object, model object, or model name
 * @param mixed $user The user to check the authorization of
 * @return boolean True if $user is authorized, otherwise false
 * @access public
 */
	function isAuthorized($type = null, $object = null, $user = null) {

		if (empty($user) && !$this->user()) {
			return false;
		} elseif (empty($user)) {
			$user = $this->user();		
		}
		extract($this->__authType($type));
	
		if (!$object) {
			$object = $this->object;
		}
		
/* 		print_r($user);
print_r($object);
echo $this->action(); */
        $user = $user['user']['groupid'];
/* 	echo $user;	 */
	
		$valid = false;
		switch ($type) {
			case 'controller':
				$valid = $object->isAuthorized();
			break;
			case 'actions':
				$valid = $this->Acl->check($user, $this->action());
			break;
			case 'crud':
				if (!isset($this->actionMap[$this->params['action']])) {
					trigger_error(
						sprintf('Auth::startup() - Attempted access of un-mapped action "%1$s" in controller "%2$s"', $this->params['action'], $this->params['controller']),
						E_USER_WARNING
					);
				} else {
					$valid = $this->Acl->check(
						$user,
						$this->action(':controller'),
						$this->actionMap[$this->params['action']]
					);
				}
			break;
			case 'model':
				$action = $this->params['action'];
				if (isset($this->actionMap[$action])) {
					$action = $this->actionMap[$action];
				}
				if (is_string($object)) {
					$object = $this->getModel($object);
				}
			case 'object':
				if (!isset($action)) {
					$action = $this->action(':action');
				}
				if (empty($object)) {
					trigger_error(sprintf('Could not find %s. Set AuthComponent::$object in beforeFilter() or pass a valid object', get_class($object)), E_USER_WARNING);
					return;
				}
				if (method_exists($object, 'isAuthorized')) {
					$valid = $object->isAuthorized($user, $this->action(':controller'), $action);
				} elseif ($object) {
					trigger_error(sprintf('%s::isAuthorized() is not defined.', get_class($object)), E_USER_WARNING);
				}
			break;
			case null:
			case false:
				return true;
			break;
			default:
				trigger_error('Auth::isAuthorized() - $authorize is set to an incorrect value.  Allowed settings are: "actions", "crud", "model" or null.', E_USER_WARNING);
			break;
		}
		return $valid;
	}

/**
用来设置使用那个类或什么类型函数来判断权限
 */
	function __authType($auth = null) {
		if ($auth == null) {
			$auth = $this->authorize;
		}
		$object = null;
		if (is_array($auth)) {
			$type = key($auth);
			$object = $auth[$type];
		} else {
			$type = $auth;
			return compact('type');
		}
		return compact('type', 'object');
	}

/**
设置允许通过的action数组,即增加$this->allowedActions中的action值
 */
	function allow() {
		$args = func_get_args();
		if (empty($args) || $args == array('*')) {
			$this->allowedActions = $this->_methods;
		} else {
			if (isset($args[0]) && is_array($args[0])) {
				$args = $args[0];
			}
			$this->allowedActions = array_merge($this->allowedActions, array_map('strtolower', $args));
		}
	}

/**
设置允许通过的action数组,即去掉$this->allowedActions中的action值
 */
	function deny() {
		$args = func_get_args();
		if (isset($args[0]) && is_array($args[0])) {
			$args = $args[0];
		}
		foreach ($args as $arg) {
			$i = array_search(strtolower($arg), $this->allowedActions);
			if (is_int($i)) {
				unset($this->allowedActions[$i]);
			}
		}
		$this->allowedActions = array_values($this->allowedActions);
	}

/**
 * Maps action names to CRUD operations. Used for controller-based authentication.
 *
 * @param array $map Actions to map
 * @return void
 * @access public
 */
	function mapActions($map = array()) {
		$crud = array('create', 'read', 'update', 'delete');
		foreach ($map as $action => $type) {
			if (in_array($action, $crud) && is_array($type)) {
				foreach ($type as $typedAction) {
					$this->actionMap[$typedAction] = $action;
				}
			} else {
				$this->actionMap[$action] = $type;
			}
		}
	}

/**
用户登入
 */
	function login($data = null) {
		$this->__setDefaults();
		$this->_loggedIn = false;

		if (empty($data)) {
			$data = $this->data;
		}

		if ($user = $this->identify($data)) {
			$this->C_session->write($this->sessionKey, $user);
			$this->_loggedIn = true;
		}
		return $this->_loggedIn;
	}

/**
安全退出
 */
	function logout() {
		$this->__setDefaults();
		$this->C_session->delete($this->sessionKey);	
		$this->C_session->delete('Auth.redirect');
		$this->_loggedIn = false;			
		return Router::normalize($this->logoutRedirect);
	}

/**
从session中得到当前用户
 */
	function user($key = null) {
		$this->__setDefaults();
		if (!$this->C_session->check($this->sessionKey)) {
			return null;
		}
		if ($key == null) {
			$model =& $this->getModel();
			return array($model->alias => $this->C_session->read($this->sessionKey));
		} else {
			$user = $this->C_session->read($this->sessionKey);
			if (isset($user[$key])) {
				return $user[$key];
			}
			return null;
		}
	}

/**
从session中得到Auth.redirect的值和设置这个值
 */
	function redirect($url = null) {
		if (!is_null($url)) {
			$redir = $url;
			$this->C_session->write('Auth.redirect', $redir);
		} elseif ($this->C_session->check('Auth.redirect')) {
			$redir = $this->C_session->read('Auth.redirect');
			$this->C_session->delete('Auth.redirect');

			if (Router::normalize($redir) == Router::normalize($this->loginAction)) {
				$redir = $this->loginRedirect;
			}
		} else {
			$redir = $this->loginRedirect;
		}
		return Router::normalize($redir);
	}


	function validate($object, $user = null, $action = null) {
		if (empty($user)) {
			$user = $this->user();
		}
		if (empty($user)) {
			return false;
		}
		return $this->Acl->check($user, $object, $action);
	}

/**
用来得到访问路径，用在权限判断里面
 */
	function action($action = '') {
	    $actionpath ='';
		if (isset($this->params['plugin'])) {
		    $actionpath .= $this->params['plugin'].'/';
		}
		if (isset($this->params['prefixes'])) {
		    $actionpath .= $this->params['prefixes'].'/';
		}		
		if (isset($this->params['controller'])) {
		    $actionpath .= $this->params['controller'].'/';
		}
		if (isset($this->params['action'])) {
		    $actionpath .= $this->params['action'];
		}
		return $actionpath;
	}

/**
产生用户存储模型
 */
	function &getModel($name = null) {
		$model = null;
		if (!$name) {
			$name = $this->userModel;
		}
		$name = strtolower($name);
		if (PHP5) {
			$model = ClassRegistry::init($name);
		} else {
			$model =& ClassRegistry::init($name);
		}
		if (empty($model)) {
			trigger_error('Auth::getModel() - Model is not set or could not be found', E_USER_WARNING);
			return null;
		}

		return $model;
	}

/**
根据条件到数据库中查询用户信息，并且返回用户信息
 */
	function identify($user = null, $conditions = null) {
		if ($conditions === false) {
			$conditions = null;
		} elseif (is_array($conditions)) {
			$conditions = array_merge((array)$this->userScope, $conditions);
		} else {
			$conditions = $this->userScope;
		}
		$model =& $this->getModel();
		if (empty($user)) {
			$user = $this->user();
			if (empty($user)) {
				return null;
			}
		} elseif (is_object($user) && is_a($user, 'Model')) {
			if (!$user->exists()) {
				return null;
			}
			$user = $user->read();
			$user = $user[$model->alias];
		} elseif (is_array($user) && isset($user[$model->alias])) {
			$user = $user[$model->alias];
		}

		if (is_array($user) && (isset($user[$this->fields['username']]) || isset($user[$model->alias . '.' . $this->fields['username']]))) {
			if (isset($user[$this->fields['username']]) && !empty($user[$this->fields['username']])  && !empty($user[$this->fields['password']])) {
				if (trim($user[$this->fields['username']]) == '=' || trim($user[$this->fields['password']]) == '=') {
					return false;
				}
				if(isset($this->fields['otherfiledslogin']) && !empty($this->fields['otherfiledslogin'])){
					$orfind = array();
					$orfind[$model->alias.'.'.$this->fields['username']] = $user[$this->fields['username']];
                    foreach($this->fields['otherfiledslogin'] as $v){
					    $orfind[$model->alias.'.'.$v] = $user[$this->fields['username']];	                    
                    }
					$find = array(
						'or' => $orfind,
						$model->alias.'.'.$this->fields['password'] => $user[$this->fields['password']]
					);					
				}else{
					$find = array(
						$model->alias.'.'.$this->fields['username'] => $user[$this->fields['username']],
						$model->alias.'.'.$this->fields['password'] => $user[$this->fields['password']]
					);					
				}
			} elseif (isset($user[$model->alias . '.' . $this->fields['username']]) && !empty($user[$model->alias . '.' . $this->fields['username']])) {
				if (trim($user[$model->alias . '.' . $this->fields['username']]) == '=' || trim($user[$model->alias . '.' . $this->fields['password']]) == '=') {
					return false;
				}
				if(isset($this->fields['otherfiledslogin']) && !empty($this->fields['otherfiledslogin'])){
					$orfind = array();
					$orfind[$model->alias.'.'.$this->fields['username']] = $user[$model->alias . '.' . $this->fields['username']];
                    foreach($this->fields['otherfiledslogin'] as $v){
					    $orfind[$model->alias.'.'.$v] = $user[$model->alias . '.' . $this->fields['username']];	                    
                    }
					$find = array(
						'or' => $orfind,
						$model->alias.'.'.$this->fields['password'] => $user[$model->alias . '.' . $this->fields['password']]
					);					
				}else{
					$find = array(
						$model->alias.'.'.$this->fields['username'] => $user[$model->alias . '.' . $this->fields['username']],
						$model->alias.'.'.$this->fields['password'] => $user[$model->alias . '.' . $this->fields['password']]
					);				
				}			

			} else {
				return false;
			}
			$query = array(
				'conditions' => array_merge($find, $conditions),
			);			
			$sql ='[@@:UM '.$model->name.'][@@:QU find:first]';
		    $data = POP::exe($sql,$query);			
			if (empty($data) || empty($data[$model->alias])) {
				return null;
			}
		} elseif (!empty($user) && is_string($user)) {
			$query = array(
				'conditions' => array_merge(array($model->escapeField() => $user), $conditions),
			);	
			$sql ='[@@:UM '.$model->name.'][@@:QU find:first]';
		    $data = POP::exe($sql,$query);				
			if (empty($data) || empty($data[$model->alias])) {
				return null;
			}
		}
		if (!empty($data)) {
			if (!empty($data[$model->alias][$this->fields['password']])) {
				unset($data[$model->alias][$this->fields['password']]);
			}
			return $data[$model->alias];
		}
		return null;
	}

/**
给密码加密
 */
	function hashPasswords($data) {
		if (is_object($this->authenticate) && method_exists($this->authenticate, 'hashPasswords')) {
			return $this->authenticate->hashPasswords($data);
		}

		if (is_array($data)) {
			$model =& $this->getModel();			
			if(isset($data[$model->alias])) {
				if (isset($data[$model->alias][$this->fields['username']]) && isset($data[$model->alias][$this->fields['password']])) {
					$data[$model->alias][$this->fields['password']] = $this->password($data[$model->alias][$this->fields['password']]);
				}
			}
		}
		return $data;
	}

/**
密码加密
 */
	function password($password) {
		return Security::hash($password, null, true);
	}

/**
用户处于登入状态时，将其session的Auth.redirect这个值去掉
 */
	function shutdown(&$controller) {
		if ($this->_loggedIn) {
			$this->C_session->delete('Auth.redirect');
		}
	}
}
